/************************************************************************
Copyright 2003, 2004, 2005, 2006, 2007, 2008 Grid Technology Research Center,
National Institute of Advanced Industrial Science and Technology.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
************************************************************************/

package jp.aist.gtrc.plus.reserve;

import java.io.Serializable;
import java.util.Collection;
import java.util.LinkedHashSet;

import jp.aist.gtrc.plus.scheduler.node.NodeInfo;
import jp.aist.gtrc.plus.scheduler.node.NodeManager;
import jp.aist.gtrc.plus.scheduler.util.TextUtil;

public class ReserveNodeSet implements Serializable {

	private static final long serialVersionUID = -38145841282039759L;

	private transient LinkedHashSet<NodeInfo> nodes;
	private transient NodeManager nodeMgr;
	private String[] nodeNames;
	private boolean resetted;
	private boolean nodeSkip;

	private void init(NodeManager nodeMgr, String[] nodeNames) {

		this.nodes = new LinkedHashSet<NodeInfo>();
		this.nodeMgr = nodeMgr;
		this.nodeNames = (nodeNames != null) ? nodeNames : new String[]{};
		this.resetted = false;
		this.nodeSkip = false;

	}

	ReserveNodeSet(NodeManager nodeMgr) {

		init(nodeMgr, null);

	}

	ReserveNodeSet(String[] nodeNames) {

		init(null, nodeNames);

	}

	void recoverTransient(NodeManager nodeMgr) {

		this.nodes = new LinkedHashSet<NodeInfo>();
		this.nodeMgr = nodeMgr;
		for (String name : nodeNames) {
			NodeInfo info = nodeMgr.getNode(name);
			if (info != null) {
				nodes.add(info);
			}
		}

	}

	void add(Collection<NodeInfo> addNodes) {

		if (addNodes != null) {
			for (NodeInfo info : addNodes) {
				if (nodes.contains(info) == false) {
					nodes.add(info);
					resetted = false;
				}
			}
			resetNodeNames();
		}

	}

	private void reset() {

		resetted = true;

		/*
		 * Assume nodeMgr.getNodes() returns
		 * 		{ "node00", "node01", "node02", "node03", ... }.
		 * If 'nodes' is
		 *     { "node02", "node01", "node00", "node03", ... },
		 * remake 'nodes' as
		 * 		{ "node00", "node01", "node02", "node03", ... }.
		 * If 'nodes' is
		 * 		{ "node02", "node01", "node03", ...},
		 * don't change 'nodes', because it's excludes node00 (=first node).
		 */
		int size = nodes.size();
		if (size == 0) {
			nodeSkip = false;
			return;
		}

		int matchedNum = 0;
		for (NodeInfo info : nodeMgr.getNodes()) {
			if (nodes.contains(info) == false) {
				nodeSkip = true;
				return;
			}
			matchedNum++;
			if (matchedNum >= size) {
				break;
			}
		}

		if (size > 1) {
			clear();
			for (NodeInfo info : nodeMgr.getNodes()) {
				if (nodes.size() >= size) {
					break;
				}
				nodes.add(info);
			}
			resetNodeNames();
		}
		nodeSkip = false;

	}

	Collection<NodeInfo> getAll() {

		if (resetted == false) {
			reset();
		}
		return nodes;

	}

	protected void resetNodeNames() {

		if ((nodeNames == null) || (nodeNames.length != size())) {
			nodeNames = new String[size()];
		}
		int i = 0;
		for (NodeInfo info : getAll()) {
			nodeNames[i++] = info.getName();
		}

	}

	public String[] getNames() {

		return TextUtil.cloneStringArray(nodeNames);

	}

	public int size() {

		return nodes.size();

	}

	public boolean isOverlap(ReserveNodeSet other) {

		for (NodeInfo n : other.getAll()) {
			if (nodes.contains(n)) {
				return true;
			}
		}
		return false;

	}

	void clear() {

		nodes.clear();
		nodeNames = new String[]{};
		nodeSkip = false;
		resetted = true;

	}

	boolean isNodeSkipped() {

		if (resetted == false) {
			reset();
		}
		return nodeSkip;

	}

	public boolean equals(Object o) {

		if (o instanceof ReserveNodeSet) {
			ReserveNodeSet other = (ReserveNodeSet)o;
			if (this.size() != other.size()) {
				return false;
			}
			for (int i = 0; i < this.nodeNames.length; i++) {
				String n1 = this.nodeNames[i];
				String n2 = other.nodeNames[i];
				if (n1.equals(n2) == false) {
					return false;
				}
			}
			return true;
		} else {
			return false;
		}

	}

	public int hashCode() {

		return super.hashCode();

	}

	public String toString() {

		StringBuffer sb = new StringBuffer();
		sb.append("nodes=");
		sb.append(TextUtil.fromArray(nodeNames, " "));
		sb.append(", resetted=");
		sb.append(resetted);
		sb.append(", nodeSkip=");
		sb.append(nodeSkip);
		return sb.toString();

	}

}
